package com.kuang.rwlock;


import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * ReadWriteLock 读写锁
 *  读-读 可以共存！
 *  读-写 可以共存！
 *  写-写 不能共存！
 *  独占锁（写锁）：一次只能被一个线程占有！
 *  共享锁（读锁）：多个线程可以同时占用！
 *
 * 需求：
 *   想要写的时候，只有一个线程写，读的时候，可以多个线程读（写的时候，希望是原子性操作）
 *   使用 sychronized 锁的是方法的调用者， 使用 static sychronized 锁的是Class模板，调用这些方法都会锁住，
 *   但是我们想要更细力度控制：写的时候，只有一个线程写，读的时候，可以多个线程读，
 *   所以，使用读写锁
 *
 *
 *
 *
 */
public class ReadWriteLockDemo {
    public static void main(String[] args) {

        // 无锁时
        //MyCache myCache = new MyCache();
        //// 写入
        //for (int i = 1; i <= 5; i++) {
        //    final int finalI = i;
        //    new Thread(()->{
        //        myCache.put(finalI+"", "值");
        //    },"写线程-"+i).start();
        //}
        //// 读取
        //for (int i = 1; i <= 5; i++) {
        //    final int finalI = i;
        //    new Thread(()->{
        //        myCache.get(finalI+"");
        //    },"读线程-"+i).start();
        //}


        // 读写锁时
        MyCacheWithReadWriteLock myCache = new MyCacheWithReadWriteLock();
        // 写入
        for (int i = 1; i <= 5; i++) {
            final int finalI = i;
            new Thread(()->{
                myCache.put(finalI+"", "值");
            },"写线程-"+i).start();
        }
        // 读取
        for (int i = 1; i <= 5; i++) {
            final int finalI = i;
            new Thread(()->{
                myCache.get(finalI+"");
            },"读线程-"+i).start();
        }

        /*
        实现效果：写线程原子性操作，读线程随意读取，所有人都可以读，可以随意插队
            写线程-1写入key=1,value=值
            写线程-1写入ok
            写线程-4写入key=4,value=值
            写线程-4写入ok
            写线程-5写入key=5,value=值
            写线程-5写入ok
            写线程-2写入key=2,value=值
            写线程-2写入ok
            写线程-3写入key=3,value=值
            写线程-3写入ok
            读线程-2读取2
            读线程-2读取ok值
            读线程-3读取3
            读线程-1读取1
            读线程-5读取5
            读线程-1读取ok值
            读线程-3读取ok值
            读线程-4读取4
            读线程-5读取ok值
            读线程-4读取ok值

         */
    }

}


/*
    自定义缓存  读写锁
 */
class MyCacheWithReadWriteLock{

    private volatile Map<String, Object> map = new HashMap<>();
    //private Lock lock = new ReentrantLock();
    // 读写锁：更细粒度控制加锁
    private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    // 存，写入的时候，只希望同时只有一个线程写！！！
    public void put(String key, Object value){
        // 写锁加锁
        readWriteLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "写入key=" + key + ",value=" + value);
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + "写入ok");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            // 读写锁释放锁
            readWriteLock.writeLock().unlock();
        }
    }

    // 取，读的时候，希望所有人都能读！！！
    public void get(String key){
        // 读锁加锁
        readWriteLock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "读取" + key);
            Object o = map.get(key);
            System.out.println(Thread.currentThread().getName() + "读取ok"+o);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            readWriteLock.readLock().unlock();
        }
    }
}



/*
    自定义缓存  无锁的
 */
class MyCache{

    private volatile Map<String, Object> map = new HashMap<>();

    // 存，写
    public void put(String key, Object value){
        System.out.println(Thread.currentThread().getName() + "写入key=" + key + ",value=" + value);
        map.put(key, value);
        System.out.println(Thread.currentThread().getName() + "写入ok");
    }

    // 取，读
    public void get(String key){
        System.out.println(Thread.currentThread().getName() + "读取" + key);
        Object o = map.get(key);
        System.out.println(Thread.currentThread().getName() + "读取ok"+o);
    }

    /*
        预期出现：1写入，1写入完成， 2写入，2写入完成，...
        出现结果：1还没有写入oK,5写入，写的时候会被插队
        这样会有什么问题吗？ 同时写入,CPU切换过于频繁，你想要的效果是，一个写入完成后写入下一个，所以没有达到预期效果

        写线程-1写入key=1value=值
        写线程-5写入key=5value=值
        写线程-5写入ok
        写线程-4写入key=4value=值
        写线程-3写入key=3value=值
        写线程-2写入key=2value=值
        写线程-3写入ok
        写线程-4写入ok
        写线程-1写入ok
        写线程-2写入ok
        读线程-1读取1
        读线程-2读取2
        读线程-1读取ok
        读线程-2读取ok
        读线程-3读取3
        读线程-3读取ok
        读线程-4读取4
        读线程-5读取5
        读线程-4读取ok
        读线程-5读取ok
     */
}
